package cgin

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

type H map[string]interface{}

type Context struct {
	Writer     http.ResponseWriter
	Req        *http.Request
	Path       string
	Method     string
	StatusCode int
	Params     map[string]string
	handlers   []HandlerFunc
	index      int
	body       []byte
}

func NewContext(w http.ResponseWriter, r *http.Request) *Context {
	return &Context{
		Writer: w,
		Req:    r,
		Path:   r.URL.Path,
		Method: r.Method,
		index:  -1,
	}
}
func (c *Context) Next() {
	c.index++
	s := len(c.handlers)
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

// 读取请求体数据
func (c *Context) readBody() ([]byte, error) {
	if c.body != nil {
		return c.body, nil
	}

	body, err := io.ReadAll(c.Req.Body)
	if err != nil {
		return nil, err
	}

	defer c.Req.Body.Close()
	c.body = body
	return body, nil
}

// BindJSON 将请求体 JSON 绑定到结构体
func (c *Context) BindJSON(obj interface{}) error {
	body, err := c.readBody()
	if err != nil {
		return fmt.Errorf("read body error: %v", err)
	}

	if err := json.Unmarshal(body, obj); err != nil {
		return fmt.Errorf("json unmarshal error: %v", err)
	}

	return nil
}

// GetBody 获取原始请求体数据
func (c *Context) GetBody() ([]byte, error) {
	return c.readBody()
}

// ShouldBindJSON 便捷方法，自动处理错误
func (c *Context) ShouldBindJSON(obj interface{}) {
	if err := c.BindJSON(obj); err != nil {
		c.Fail(400, err.Error())
	}
}

// GetJSON 获取 JSON 中的特定字段
func (c *Context) GetJSON(key string) interface{} {
	var data map[string]interface{}
	if err := c.BindJSON(&data); err != nil {
		return nil
	}
	return data[key]
}

func (c *Context) Param(key string) string {
	value, _ := c.Params[key]
	return value
}

func (c *Context) PostForm(key string) string {
	return c.Req.FormValue(key)
}

func (c *Context) Query(key string) string {
	return c.Req.URL.Query().Get(key)
}

func (c *Context) Status(code int) {
	c.StatusCode = code
	c.Writer.WriteHeader(code)
}
func (c *Context) SetHeader(key string, value string) {
	c.Writer.Header().Set(key, value)
}

func (c *Context) String(code int, format string, values ...interface{}) {
	c.SetHeader("Content-Type", "text/plain; charset=utf-8")
	c.Status(code)
	c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
}

func (c *Context) JSON(code int, obj interface{}) {
	c.SetHeader("Content-Type", "application/json; charset=utf-8")
	c.Status(code)
	encoder := json.NewEncoder(c.Writer)
	if err := encoder.Encode(obj); err != nil {
		http.Error(c.Writer, err.Error(), 500)
	}
}

func (c *Context) Data(code int, data []byte) {
	c.Status(code)
	c.Writer.Write(data)
}

func (c *Context) HTML(code int, html string) {
	c.SetHeader("Content-Type", "text/html; charset=utf-8")
	c.Status(code)
	c.Writer.Write([]byte(html))
}

func (c *Context) Fail(code int, err string) {
	c.index = len(c.handlers)
	c.JSON(code, H{"error": err})
}
